/*
 * This file is part of aion-engine <aion-engine.com>
 *
 * aion-engine is private software: you can redistribute it and or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Private Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * aion-engine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser Public License
 * along with aion-engine.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.aionengine.gameserver.world.geo;

import com.aionengine.gameserver.configs.main.GeoDataConfig;
import com.aionengine.gameserver.geoEngine.collision.CollisionResults;
import com.aionengine.gameserver.geoEngine.math.Vector3f;
import com.aionengine.gameserver.model.gameobjects.Creature;
import com.aionengine.gameserver.model.gameobjects.VisibleObject;
import com.aionengine.gameserver.utils.MathUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author ATracer
 */
public class GeoService {

    private static final Logger log = LoggerFactory.getLogger(GeoService.class);
    private GeoData geoData;

    /**
     * Initialize geodata based on configuration, load necessary structures
     */
    public void initializeGeo() {
        switch (getConfiguredGeoType()) {
            case GEO_MESHES:
                geoData = new RealGeoData();
                break;
            case NO_GEO:
                geoData = new DummyGeoData();
                break;
        }
        log.info("Configured Geo type: " + getConfiguredGeoType());
        geoData.loadGeoMaps();
    }

    public void setDoorState(int worldId, int instanceId, String name, boolean isOpened) {
        if (GeoDataConfig.GEO_ENABLE) {
            geoData.getMap(worldId).setDoorState(instanceId, name, isOpened);
        }
    }

    /**
     * @param object
     * @return
     */
    public float getZAfterMoveBehind(int worldId, float x, float y, float z, int instanceId) {
        if (GeoDataConfig.GEO_ENABLE) {
            return getZ(worldId, x, y, z, 0, instanceId);
        }
        return getZ(worldId, x, y, z, 0.5f, instanceId);
    }

    /**
     * @param object
     * @return
     */
    public float getZ(VisibleObject object) {
        return geoData.getMap(object.getWorldId()).getZ(object.getX(), object.getY(), object.getZ(), object.getInstanceId());
    }

    /**
     * @param worldId
     * @param x
     * @param y
     * @param z
     * @param defaultUp
     * @return
     */
    public float getZ(int worldId, float x, float y, float z, float defaultUp, int instanceId) {
        float newZ = geoData.getMap(worldId).getZ(x, y, z, instanceId);
        if (!GeoDataConfig.GEO_ENABLE) {
            newZ += defaultUp;
        }/*
        else {
			newZ += 0.5f;			
		}*/
        return newZ;
    }

    /**
     * @param worldId
     * @param x
     * @param y
     * @return
     */
    public float getZ(int worldId, float x, float y) {
        return geoData.getMap(worldId).getZ(x, y);
    }

    public String getDoorName(int worldId, String meshFile, float x, float y, float z) {
        return geoData.getMap(worldId).getDoorName(worldId, meshFile, x, y, z);
    }

    public CollisionResults getCollisions(VisibleObject object, float x, float y, float z, boolean changeDirection, byte intentions) {
        return geoData.getMap(object.getWorldId()).getCollisions(object.getX(), object.getY(), object.getZ(), x, y, z, changeDirection, false, object.getInstanceId(), intentions);
    }

    /**
     * @param object
     * @param target
     * @return
     */
    public boolean canSee(VisibleObject object, VisibleObject target) {
        if (!GeoDataConfig.CANSEE_ENABLE) {
            return true;
        }
        float limit = (float) (MathUtil.getDistance(object, target) - target.getObjectTemplate().getBoundRadius().getCollision());
        if (limit <= 0)
            return true;
        return geoData.getMap(object.getWorldId()).canSee(object.getX(), object.getY(),
                object.getZ() + object.getObjectTemplate().getBoundRadius().getUpper() / 2, target.getX(), target.getY(),
                target.getZ() + target.getObjectTemplate().getBoundRadius().getUpper() / 2, limit, object.getInstanceId());
    }

    public boolean canSee(int worldId, float x, float y, float z, float x1, float y1, float z1, float limit, int instanceId) {
        return geoData.getMap(worldId).canSee(x, y, z, x1, y1, z1, limit, instanceId);
    }

    public boolean isGeoOn() {
        return GeoDataConfig.GEO_ENABLE;
    }

    public Vector3f getClosestCollision(Creature object, float x, float y, float z, boolean changeDirection, byte intentions) {
        return geoData.getMap(object.getWorldId()).getClosestCollision(object.getX(), object.getY(), object.getZ(), x, y, z, changeDirection,
                object.isInFlyingState(), object.getInstanceId(), intentions);
    }

    public GeoType getConfiguredGeoType() {
        if (GeoDataConfig.GEO_ENABLE) {
            return GeoType.GEO_MESHES;
        }
        return GeoType.NO_GEO;
    }

    public static final GeoService getInstance() {
        return SingletonHolder.instance;
    }

    @SuppressWarnings("synthetic-access")
    private static final class SingletonHolder {

        protected static final GeoService instance = new GeoService();
    }
}
